package com.socket.client.model.chat;

import cn.hutool.core.annotation.PropIgnore;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.socket.client.model.enums.OnlineState;
import com.socket.client.util.AESUtil;
import com.socket.core.model.TokenUser;
import com.socket.core.model.command.BaseCommand;
import com.socket.core.model.command.enmus.Command;
import com.socket.core.model.command.enmus.SysLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.yeauty.pojo.Session;

import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;

/**
 * Socket用户数据
 */
@Slf4j
@NoArgsConstructor
public class ChatUser extends SysUser {
    /**
     * Ws Session
     */
    private final Map<String, Session> sessions = new ConcurrentHashMap<>();
    /**
     * 当前选择的用户UID
     */
    @Setter
    private String choose;
    /**
     * 通过token获取key
     */
    private Function<String, TokenUser> tokenLambda;
    /**
     * 用户信息扩展
     */
    @Getter
    @PropIgnore
    private UserExt ext;

    /**
     * 构建Socket用户
     *
     * @param info 用户信息
     */
    public ChatUser(SysUser info) {
        BeanUtil.copyProperties(info, this);
    }

    /**
     * 设置登录数据
     *
     * @param session     Socket会话
     * @param ext         {@link UserExt}
     * @param tokenLambda {@link TokenUser}
     */
    public void login(Session session, UserExt ext, Function<String, TokenUser> tokenLambda) {
        sessions.put(session.id().asLongText(), session);
        this.ext = ext;
        this.tokenLambda = tokenLambda;
        super.setOnline(OnlineState.ONLINE);
    }

    /**
     * 测试当前用户会话是否指定目标用户（在线状态）
     *
     * @param target 用户信息
     * @return 选择返回true
     */
    public boolean chooseTarget(ChatUser target) {
        return Objects.equals(target.getGuid(), choose);
    }

    /**
     * 发送回调通知
     *
     * @param callback 回调消息
     * @param command  消息类型
     * @param data     额外数据
     */
    public void send(String callback, BaseCommand<?> command, Object data) {
        this.send(new ChatMessage(callback, command, data));
    }

    /**
     * 发送回调通知
     *
     * @param callback 回调消息
     * @param command  消息类型
     */
    public void send(String callback, BaseCommand<?> command) {
        this.send(callback, command, null);
    }

    /**
     * 发送拒绝消息
     *
     * @param reason  原因
     * @param message 消息
     */
    public void reject(String reason, ChatMessage message) {
        this.send(reason, SysLevel.WARNING);
        message.setReject(true);
        this.send(message);
    }

    /**
     * 注销Session会话
     *
     * @param session Socket会话
     */
    public void logout(Session session) {
        String key = session.id().asLongText();
        Optional.ofNullable(sessions.remove(key)).ifPresent(Session::close);
        if (sessions.isEmpty()) {
            super.setOnline(null);
        }
    }

    /**
     * 注销所有Session会话
     *
     * @param reason 原因
     * @param param  格式化参数
     */
    public void logout(String reason, Object... param) {
        this.send(StrUtil.format(reason, param), Command.CLOSE);
        sessions.values().forEach(Session::close);
        sessions.clear();
    }

    /**
     * 将消息发送至目标用户（目标不在线调用此方法没有任何效果）
     *
     * @param message 消息
     */
    public void send(ChatMessage message) {
        if (isOnline()) {
            TokenUser user = tokenLambda.apply(ext.getToken());
            if (user != null) {
                // 加密消息
                String json = JSONUtil.toJsonStr(message);
                String enmsg = AESUtil.encrypt(json, user.getKey());
                sessions.values().forEach(session -> session.sendText(enmsg));
            }
        }
    }

    /**
     * 解密消息
     *
     * @param message 消息
     * @return 转换后消息
     */
    public Optional<ChatMessage> decrypt(String message) {
        TokenUser user = tokenLambda.apply(ext.getToken());
        if (user != null) {
            // 解密消息
            String decrypt = AESUtil.decrypt(message, user.getKey());
            ChatMessage convert = JSONUtil.parseObj(decrypt).toBean(ChatMessage.class);
            convert.setGuid(getGuid());
            return Optional.of(convert);
        }
        return Optional.empty();
    }

    /**
     * 判断当前用户是否在线
     */
    public boolean isOnline() {
        return !sessions.isEmpty();
    }

    /**
     * 检查登录令牌是否相同
     */
    public boolean differentToken(String token) {
        return !Objects.equals(token, ext.getToken());
    }

    @Override
    public String toString() {
        return super.toString();
    }

}
